﻿package com.zouloux.physics 
{
	import com.zouloux.physics.events.PhysicsEvent;
	import flash.events.Event;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class PhysicsSphere extends APhysicsObject implements IPhysicsObject
	{
		// Le rayon de cette sphère
		protected var _radius:Number = 0;
		
		
		// Les getters
		public function get radius():Number
		{
			return _radius;
		}
		
		
		// Les setters
		public function set radius(value:Number):void 
		{
			if (value >= 0)
				_radius = value;
			else throw new Error("A radius can't be negative");
		}
		
		
		// Le constructeur
		public function PhysicsSphere (pRadius:Number = 0)
		{
			// Construire le parent
			super();
			
			// Enregistrer le rayon
			radius = pRadius;
		}
		
		// Actualiser les propriétés physiques
		override public function update ():void
		{
			// Vérifier si le moteur est activé
			if (physicEnabled)
			{
				// On passe le boulot à la classe d'abstraction
				super.update();
				
				// Récupérer le rectangle des limites
				var limits:Rectangle = engine.limits;
				
				// Collision gauche
				if (limits.x >= x - radius)
				{
					// Rebond
					x = limits.x + radius;
					_velocityX = Math.abs(_velocityX);
					
					// Freiner
					frictionBreak();
					
					// Envoyer la collision
					sendWallCollision(270, _velocityX);
				}
				
				// Collision droite
				if (limits.width <= x + radius)
				{
					// Rebond
					x = limits.width - radius;
					_velocityX = -Math.abs(_velocityX);
					
					// Freiner
					frictionBreak();
					
					// Envoyer la collision
					sendWallCollision(90, _velocityX);
				}
				
				// Collision haut
				if (limits.y >= y - radius)
				{
					// Rebond
					y = limits.y + radius;
					_velocityY = Math.abs(_velocityY);
					
					// Freiner
					frictionBreak();
					
					// Envoyer la collision
					sendWallCollision(0, _velocityY);
				}
				
				// Collision bas
				if (limits.height <= y + radius)
				{
					// Rebond
					y = limits.height - radius;
					_velocityY = -Math.abs(_velocityY);
					
					// Freiner
					frictionBreak();
					
					// Envoyer la collision
					sendWallCollision(180, _velocityY);
				}
			}
		}
		
		// Le frein
		protected function frictionBreak ():void
		{
			_velocityX /= _friction + 1;
			_velocityY /= _friction + 1;
		}
		
		// Signaler la collision avec un mur
		protected function sendWallCollision (pAngle:Number, pVelocity:Number):void
		{
			// Créer l'event
			var event:PhysicsEvent = new PhysicsEvent(PhysicsEvent.COLLISION);
			
			// Le paramètrer
			event.angle = pAngle;
			event.correction = Math.abs(pVelocity);
			
			// L'envoyer
			dispatchEvent(event);
		}
		
		// Vérifier la collision avec un autre objet
		override public function checkCollisionWidth (pPhysicsObject:IPhysicsObject):void
		{
			// Vérifier si c'est une sphère et si le moteur est activé
			if (pPhysicsObject is PhysicsSphere && physicEnabled)
			{
				// Cibler le nouveau typage de l'objet b
				var b:PhysicsSphere = (pPhysicsObject as PhysicsSphere);
				
				// Calculer les distances en x et en y
				var distanceX:Number = (b.x - x);
				var distanceY:Number = (b.y - y);
				
				// Calculer la distance entre le point A et le point B
				var distance:Number = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
				
				// Détécter la collision par la distance
				if (distance <= b.radius + radius)
				{
					// Calculer l'angle entre A et B
					var angle:Number = Math.atan2(distanceX, distanceY);
					
					// Calculer la correction à apporter
					var correction:Number = distance - (b.radius + radius);
					
					// La correction en x et en y
					var xCorrection:Number = Math.sin(angle) * correction / 2;
					var yCorrection:Number = Math.cos(angle) * correction / 2;
					
					// Appliquer la correction sur A
					x += xCorrection;
					y += yCorrection;
					
					// Appliquer la correction sur B
					b.x -= xCorrection;
					b.y -= yCorrection;
					
					// Appliquer les nouvelles vélocités à B
					b.velocityX -= -Math.sin(angle) * speed * _friction;
					b.velocityY -= -Math.cos(angle) * speed * _friction;
					
					velocityX -= Math.sin(angle) * speed * _friction;
					velocityY -= Math.cos(angle) * speed * _friction;
					
					// Freiner
					velocityY *= .8;
					velocityX *= .8;
					
					// Déclancher l'évènement
					var event:PhysicsEvent = new PhysicsEvent(PhysicsEvent.COLLISION);
					
					// Définir les variables
					event.angle = angle;
					event.collisionWith = b;
					event.distance = distance;
					event.correction = correction;
					
					// Le dispatcher
					dispatchEvent(event);
				}
			}
		}
	}
}